import json
import time
from flask import Flask, jsonify
import serial
from serial.tools import list_ports
from typing import Optional, List
import threading
from MQTT_util import MQTTClient
from log_util import default_logger as logger

app = Flask(__name__)

Ser = None
SERIAL_RECONNECT_INTERVAL = 2  # 串口重连间隔时间（秒）
is_trigger_pack = False

ESP_get_device_info = "get_device_ID"  # 检查可用端口
ESP_trigger_pack = "trigger_pack"  # 触发打包

MQTT_BROKER = "127.0.0.1"
MQTT_PORT = 1883
MQTT_TOPIC = "/packager/status"
mqttClient = MQTTClient(MQTT_BROKER, MQTT_PORT)

LAST_TRIGGER_TIME = 0
TRIGGER_INTERVAL = 2

lock = threading.Lock()  # 线程锁

def initialize_serial(port, baudrate=115200, timeout=SERIAL_RECONNECT_INTERVAL):
    global Ser
    try:
        Ser = serial.Serial(port, baudrate=baudrate, timeout=timeout)
        logger.info(f"串口 {port} 已成功打开，波特率：{baudrate}")
        time.sleep(1)  # 等待串口初始化完成
        return True
    except serial.SerialException as e:
        logger.error(f"无法打开串口 {port}：{e}")
        return False

def get_available_ports():
    import sys
    if sys.platform.startswith('win'):
        ports = [port.device for port in list_ports.comports()]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        ports = ['/dev/ttyUSB%s' % i for i in range(64)] + ['/dev/ttyACM%s' % i for i in range(64)]
    elif sys.platform.startswith('darwin'):
        ports = ['/dev/cu.usbmodem%s' % i for i in range(64)] + ['/dev/cu.usbserial%s' % i for i in range(64)]
    else:
        raise EnvironmentError('Unsupported platform')
    return ports

def auto_detect_port(expected_id: str = "ESP32_dabao") -> Optional[str]:
    for port in get_available_ports():
        logger.info(f"正在检测串口: {port}")
        try:
            with serial.Serial(port, baudrate=115200, timeout=SERIAL_RECONNECT_INTERVAL) as detector:
                start_time = time.time()
                while time.time() - start_time < SERIAL_RECONNECT_INTERVAL:
                    detector.write(f"get_device_ID\n".encode('utf-8'))
                    while detector.in_waiting:
                        response = detector.readline().decode('utf-8', errors='ignore').strip()
                        if response:
                            try:
                                response_data = json.loads(response)
                                if (response_data.get("command") == "get_device_ID" and
                                    response_data.get("deviceID") == expected_id and
                                    response_data.get("status") == "ok"):
                                    logger.info(f"√ 找到目标设备在 {port}")
                                    return port
                            except json.JSONDecodeError:
                                continue
                    time.sleep(0.1)
        except serial.SerialException as e:
            logger.error(f"  {port} 检测失败: {str(e)}")
            continue
    return None

def read_serial():
    global Ser, is_trigger_pack
    while True:
        try:
            if not is_trigger_pack:
                with lock:
                    Ser.flushInput()
                    if Ser is None or not Ser.is_open:
                        logger.error("串口已断开，尝试重新连接...")
                        port = auto_detect_port()
                        if port and initialize_serial(port):
                            logger.info("串口重新连接成功")
                        else:
                            logger.error("串口重新连接失败，等待重连...")
                            time.sleep(SERIAL_RECONNECT_INTERVAL)
                            continue
                    line = Ser.readline().decode('utf-8', errors='ignore').strip()
                if line.strip():
                    if 'get_status","status' in line:
                        logger.info(f"收到串口信息：{line}")
                        try:
                            data = json.loads(line)
                            if "status" in data:
                                status_data = data["status"]
                                mqttClient.publish(MQTT_TOPIC, json.dumps(status_data))
                        except json.JSONDecodeError as e:
                            logger.error(f"解析 JSON 错误：{e}")
        except Exception as e:
            logger.error(f"读取串口错误：{e}")
            Ser = None  # 标记串口为断开状态
            time.sleep(SERIAL_RECONNECT_INTERVAL)

@app.route('/trigger_pack', methods=['GET'])
def trigger_pack():
    global LAST_TRIGGER_TIME
    global Ser, is_trigger_pack

    is_trigger_pack = True

    if Ser is None or not Ser.is_open:
        return jsonify({
            "code": 500,
            "msg": "串口未初始化或已断开"
        }), 500

    current_time = time.time()
    if current_time - LAST_TRIGGER_TIME < TRIGGER_INTERVAL:
        return jsonify({
            "code": 429,
            "data": False,
            "msg": "操作过于频繁，请间隔2秒后重试"
        }), 429

    command = "trigger_pack\n".encode('utf-8')
    with lock:
        Ser.write(command)
        time.sleep(0.1)
        start_time = time.time()
        response = ''
        while time.time() - start_time < 5:
            line = Ser.readline().decode('utf-8', errors='ignore').strip()
            if line:
                response = line
                break
            time.sleep(0.001)
    LAST_TRIGGER_TIME = current_time
    is_trigger_pack = False

    if response:
        return jsonify({
            "code": 200,
            "data": True,
            "msg": "封口命令执行成功，收到设备响应"
        }), 200
    else:
        return jsonify({
            "code": 200,
            "data": True,
            "msg": "封口命令执行成功，但未收到设备响应"
        }), 200

if __name__ == '__main__':
    port = auto_detect_port()
    if port:
        if initialize_serial(port) and mqttClient.connect():
            serial_thread = threading.Thread(target=read_serial)
            serial_thread.daemon = True
            serial_thread.start()

            app.run(host='0.0.0.0', port=8085, debug=False)
    else:
        logger.error("串口初始化失败，无法启动 Flask 应用")